package com.zf.yichat.utils.HJpay.com.sdk.utils;


import com.zf.yichat.utils.HJpay.com.sdk.entity.Request;
import com.zf.yichat.utils.HJpay.com.sdk.entity.Response;
import com.zf.yichat.utils.HJpay.com.sdk.enums.SignTypeEnum;
import com.zf.yichat.utils.HJpay.com.sdk.exceptions.JPException;

import java.lang.reflect.Field;
import java.util.*;

/**
 * 签名、验签的工具类
 * @author chenyf
 * @date 2018-12-15
 */
public class SignUtil {
    public final static String SIGN_SEPARATOR = "&";//分隔符
    public final static String SIGN_EQUAL = "=";//等于号
    public final static String SIGN_KEY_PARAM_NAME = "key";
    public final static List<String> NOT_SIGN_PARAM = Arrays.asList(new String[]{"sign","aesKey","aes_key","sec_key"});//不参与签名/验签的参数

    /**
     * 验证签名
     * @param response
     * @param key
     * @return
     */
    public static boolean verify(Response response, String key) throws Exception {
        String signStr = getSortedString(response);
        if(SignTypeEnum.MD5.getValue().equals(response.getSign_type())){
            signStr = HEXUtil.encode(genMD5Sign(signStr, key), true);
            if(signStr.equals(response.getSign())){
                return true;
            }else{
                return false;
            }
        }else if(SignTypeEnum.RSA.getValue().equals(response.getSign_type())){
            return RSAUtil.verify(signStr, key, HEXUtil.decode(response.getSign()), true);
        }else{
            return false;
        }
    }

    /**
     * 生成签名
     * @param request
     * @param key
     * @return
     */
    public static void sign(Request request, String key) throws Exception {
        if(StringUtil.isEmpty(request)){
            return;
        }else if(StringUtil.isEmpty(request.getMch_id()) || StringUtil.isEmpty(request.getSign_type())){
            request.setSign("");
            return;
        }

        if(StringUtil.isEmpty(request.getRand_str())){
            request.setRand_str(RandomUtil.get32LenStr());
        }

        String signStr = getSortedString(request);

        if(SignTypeEnum.MD5.getValue().equals(request.getSign_type())){
            signStr = HEXUtil.encode(genMD5Sign(signStr, key), true);
        }else if(SignTypeEnum.RSA.getValue().equals(request.getSign_type())){
            signStr = HEXUtil.encode(RSAUtil.sign(signStr, key, true));
        }else{
            //抛出签名失败的异常
            throw new JPException("签名失败，未预期的签名类型："+request.getSign_type());
        }
        request.setSign(signStr);
    }

    protected static String getSortedString(Object obj) throws Exception {
        Field[] fields = obj.getClass().getDeclaredFields();

        Map<String, String> map = new HashMap<String, String>();
        for(int i=0; i<fields.length; i++){
            Field filed = fields[i];
            String name = filed.getName();
            if(NOT_SIGN_PARAM.contains(name)){//不参与签名或验签的参数直接跳过
                continue;
            }
            filed.setAccessible(true);
            map.put(name, String.valueOf(filed.get(obj)));
        }

        StringBuffer content = new StringBuffer();
        List<String> keys = new ArrayList(map.keySet());
        Collections.sort(keys); // 排序map
        for (int i = 0; i < keys.size(); i++) {
            String key = keys.get(i);
            String value = map.get(key);

            if(i != 0){
                content.append(SIGN_SEPARATOR);
            }
            content.append(key).append(SIGN_EQUAL).append(value);
        }
        return content.toString();
    }

    private static byte[] genMD5Sign(String signStr, String key){
        return MD5Util.getMD5(signStr + SIGN_SEPARATOR + SIGN_KEY_PARAM_NAME + SIGN_EQUAL + key);
    }
}
